{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import copy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "def rank1decomp(x, it_time=100, tol=1e-15):\n",
    "    \"\"\"\n",
    "    :param x: 待分解的张量\n",
    "    :param it_time: 最大迭代步数\n",
    "    :param tol: 迭代终止的阈值\n",
    "    :return vs: 储存rank-1分解各个向量的list\n",
    "    :return k: rank-1系数\n",
    "    \"\"\"\n",
    "    ndim = x.ndim  # 读取张量x的阶数\n",
    "    dims = x.shape  # 读取张量x各个指标的维数\n",
    "\n",
    "    # 初始化vs中的各个向量并归一化\n",
    "    vs = list()  # vs用以储存rank-1分解得到的各个向量\n",
    "    for n in range(ndim):\n",
    "        _v = np.random.randn(dims[n])\n",
    "        vs.append(_v / np.linalg.norm(_v))\n",
    "    k = 1\n",
    "\n",
    "    for t in range(it_time):\n",
    "        vs0 = copy.deepcopy(vs)  # 暂存各个向量以计算收敛情况\n",
    "        for _ in range(ndim):\n",
    "            # 收缩前(ndim-1)个向量，更新最后一个向量\n",
    "            x1 = copy.deepcopy(x)\n",
    "            for n in range(ndim-1):\n",
    "                x1 = np.tensordot(x1, vs[n], [[0], [0]])\n",
    "            # 归一化得到的向量，并更新常数k\n",
    "            k = np.linalg.norm(x1)\n",
    "            x1 /= k\n",
    "            # 将最后一个向量放置到第0位置\n",
    "            vs.pop()\n",
    "            vs.insert(0, x1)\n",
    "            # 将张量最后一个指标放置到第0位置\n",
    "            x = x.transpose([ndim-1] + list(range(ndim-1)))\n",
    "        # 计算收敛情况\n",
    "        conv = np.linalg.norm(np.hstack(vs0) - np.hstack(vs))\n",
    "        if conv < tol:\n",
    "            break\n",
    "    return vs, k"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The vectors by rank-1 decomposition = \n",
      "[-0.76151649  0.64814553]\n",
      "[-0.85191678  0.52367719]\n",
      "[-0.94806533 -0.31807567]\n",
      "[ 0.28278109 -0.10074216 -0.95387938]\n",
      "[ 0.60972931 -0.32183711  0.10287833 -0.71698473]\n",
      "\n",
      "The rank-1 coefficient = 3.7831923158779848\n"
     ]
    }
   ],
   "source": [
    "# 测试rank-1分解程序\n",
    "\n",
    "tensor = np.random.randn(2, 2, 2, 3, 4)\n",
    "vecs, coeff = rank1decomp(tensor)\n",
    "print('The vectors by rank-1 decomposition = ')\n",
    "for x in vecs:\n",
    "    print(x)\n",
    "print('\\nThe rank-1 coefficient = ' + str(coeff))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
